package com.storm.classes.graphics
{
	import flash.display.Graphics;
	import flash.geom.Point;
	
	public class GraphicUtils {
		public function GraphicUtils() {
		}
		
		public static function color2rgb(color:uint):Object {
			var o:Object = new Object;
			o.r = (color & 0xff0000) >> 16;
			o.g = (color & 0xff00) >> 8;
			o.b = (color & 0xff)
			return o;
		}
		
		public static function rgb2color(rgb:Object):uint {
			return (rgb.r << 16) | (rgb.g << 8) | rgb.b;
		}
		
		public static function rgb2hsb(rgb:Object):Object {
			var o:Object = {};
			var max:Number = Math.max(Math.max(rgb.r, rgb.g), rgb.b);
			var min:Number = Math.min(Math.min(rgb.r, rgb.g), rgb.b);
			o.b = max / 255;
			o.s = (max == 0) ? 0 : (max - min) / max;
			if (max == min) {
				o.h = 0;
				return o;
			}
			
			if (max == rgb.r && rgb.g >= rgb.b) {
				o.h = (rgb.g - rgb.b) * 60 / (max - min) + 0;
			} else if (max == rgb.r && rgb.g < rgb.b) {
				o.h = (rgb.g - rgb.b) * 60 / (max - min) + 360;
			} else if (max == rgb.g) {
				o.h = (rgb.b - rgb.r) * 60 / (max - min) + 120;
			} else if (max == rgb.b) {
				o.h = (rgb.r - rgb.g) * 60 / (max - min) + 240;
			}
			
			return o;
		}
		
		public static function hsb2rgb(hsb:Object):Object {
			var o:Object = {};
			var i:int = (int)(hsb.h / 60) % 6;  
			var f:Number = (hsb.h / 60) - i;  
			var p:Number = hsb.b * (1 - hsb.s);  
			var q:Number = hsb.b * (1 - f * hsb.s);  
			var t:Number = hsb.b * (1 - (1 - f) * hsb.s);  
			switch (i) {  
				case 0:  
					o.r = (int)(hsb.b * 255);  
					o.g = (int)(t * 255);  
					o.b = (int)(p * 255);  
					break;  
				case 1:  
					o.r = (int)(q * 255);  
					o.g = (int)(hsb.b * 255);  
					o.b = (int)(p * 255);  
					break;  
				case 2:  
					o.r = (int)(p * 255);  
					o.g = (int)(hsb.b * 255);  
					o.b = (int)(t * 255);  
					break;  
				case 3:  
					o.r = (int)(p * 255);  
					o.g = (int)(q * 255);  
					o.b = (int)(hsb.b * 255);  
					break;  
				case 4:  
					o.r = (int)(t * 255);  
					o.g = (int)(p * 255);  
					o.b = (int)(hsb.b * 255);  
					break;  
				case 5:  
					o.r = (int)(hsb.b * 255);  
					o.g = (int)(p * 255);  
					o.b = (int)(q * 255);  
					break;  
				default:  
					break;  
			}  
			return o;
		}
		
		public static function color2hsb(color:uint):Object {
			return rgb2hsb(color2rgb(color));
		}
		
		public static function hsb2color(hsb:Object):uint {
			return rgb2color(hsb2rgb(hsb));
		}
		
		public static function hsb2color2(h:Number, s:Number, b:Number = 0.5):uint {
			return hsb2color({h:h, s:s, b:b});
		}
		
		public static function getLight(color:uint, rate:Number = 0.5):uint {
			var hsb:Object = color2hsb(color);
			hsb.s *= (1 - rate);
			hsb.b = (1 - rate) * hsb.b + rate;
			return hsb2color(hsb);
		}
		
		public static function getDark(color:uint, rate:Number = 0.5):uint {
			var hsb:Object = color2hsb(color);
			hsb.s = (1 - rate) * hsb.s + rate;
			hsb.b *= (1 - rate);
			return hsb2color(hsb);
		}
		
		public static const PI2:Number = Math.PI * 2;
		
		public static const PI_2:Number = Math.PI * 0.5;
		public static const PI_4:Number = Math.PI * 0.25;
		public static const PI_8:Number = Math.PI * 0.125;
		
		public static const COS_PI_8:Number = Math.cos(PI_8);
		
		private static const HALF_QUOTER_ANGLES:Array = [
			0.5 * PI_4,
			1.5 * PI_4,
			2.5 * PI_4,
			3.5 * PI_4,
			4.5 * PI_4,
			5.5 * PI_4,
			6.5 * PI_4,
			7.5 * PI_4,
		];
		
		private static const FULL_QUOTER_ANGLES:Array = [
			1 * PI_4,
			2 * PI_4,
			3 * PI_4,
			4 * PI_4,
			5 * PI_4,
			6 * PI_4,
			7 * PI_4,
			0 * PI_4,
		];
		
		private static const HALF_COS_VALUES:Array = [
			Math.cos(HALF_QUOTER_ANGLES[0]),
			Math.cos(HALF_QUOTER_ANGLES[1]),
			Math.cos(HALF_QUOTER_ANGLES[2]),
			Math.cos(HALF_QUOTER_ANGLES[3]),
			Math.cos(HALF_QUOTER_ANGLES[4]),
			Math.cos(HALF_QUOTER_ANGLES[5]),
			Math.cos(HALF_QUOTER_ANGLES[6]),
			Math.cos(HALF_QUOTER_ANGLES[7]),
		];
		
		private static const FULL_COS_VALUES:Array = [
			Math.cos(FULL_QUOTER_ANGLES[0]),
			Math.cos(FULL_QUOTER_ANGLES[1]),
			Math.cos(FULL_QUOTER_ANGLES[2]),
			Math.cos(FULL_QUOTER_ANGLES[3]),
			Math.cos(FULL_QUOTER_ANGLES[4]),
			Math.cos(FULL_QUOTER_ANGLES[5]),
			Math.cos(FULL_QUOTER_ANGLES[6]),
			Math.cos(FULL_QUOTER_ANGLES[7]),
		];
		
		private static const HALF_SIN_VALUES:Array = [
			Math.sin(-HALF_QUOTER_ANGLES[0]),
			Math.sin(-HALF_QUOTER_ANGLES[1]),
			Math.sin(-HALF_QUOTER_ANGLES[2]),
			Math.sin(-HALF_QUOTER_ANGLES[3]),
			Math.sin(-HALF_QUOTER_ANGLES[4]),
			Math.sin(-HALF_QUOTER_ANGLES[5]),
			Math.sin(-HALF_QUOTER_ANGLES[6]),
			Math.sin(-HALF_QUOTER_ANGLES[7]),
		];
		
		private static const FULL_SIN_VALUES:Array = [
			Math.sin(-FULL_QUOTER_ANGLES[0]),
			Math.sin(-FULL_QUOTER_ANGLES[1]),
			Math.sin(-FULL_QUOTER_ANGLES[2]),
			Math.sin(-FULL_QUOTER_ANGLES[3]),
			Math.sin(-FULL_QUOTER_ANGLES[4]),
			Math.sin(-FULL_QUOTER_ANGLES[5]),
			Math.sin(-FULL_QUOTER_ANGLES[6]),
			Math.sin(-FULL_QUOTER_ANGLES[7]),
		];
		
		private static function drawSmallArc(g:Graphics, x:Number, y:Number, startAngle:Number, angle:Number, radius:Number, yRadius:Number = NaN):void {
			if (angle == 0) return;
			
			var halfAngle:Number = angle * 0.5;
			var midAngle:Number = startAngle + halfAngle;
			var toAngle:Number = startAngle + angle;
			
			var bx:Number = x + Math.cos(midAngle) * (radius / Math.cos(halfAngle));
			var by:Number = y + Math.sin(-midAngle) * (yRadius / Math.cos(halfAngle));
			
			var cx:Number = x + Math.cos(toAngle) * radius;
			var cy:Number = y + Math.sin(-toAngle) * yRadius;
			
			g.curveTo(bx, by, cx, cy);
		}
		
		private static function drawQuoterArc(g:Graphics, x:Number, y:Number, i:int, radius:Number, yRadius:Number = NaN, halfRadius:Number = NaN, halfYRadius:Number = NaN, dir:int = 1):void {
			var k:int;
			if (dir < 0) {
				i = (i + 7) % 8;
				k = (i + 7) % 8;
			} else {
				i %= 8;
				k = i;
			}
			
			var bx:Number = x + HALF_COS_VALUES[i] * halfRadius;
			var by:Number = y + HALF_SIN_VALUES[i] * halfYRadius;
			
			var cx:Number = x + FULL_COS_VALUES[k] * radius;
			var cy:Number = y + FULL_SIN_VALUES[k] * yRadius;
			
			g.curveTo(bx, by, cx, cy);
		}
		
		public static function drawArc(g:Graphics, x:Number, y:Number, startAngle:Number, angle:Number, radius:Number, yRadius:Number = NaN, joint:Boolean = false, reverse:Boolean = false):Point {
			if (reverse) {
				startAngle = startAngle + angle;
				angle = - angle;
			}
			
			if (isNaN(yRadius)) {
				yRadius = radius;
			}
			
			var pt:Point = new Point(x + Math.cos(startAngle) * radius, y + Math.sin(-startAngle) * yRadius);
			
			if (joint) {
				g.lineTo(pt.x, pt.y);
			} else {
				g.moveTo(pt.x, pt.y);
			}
			
			if (angle == 0) return pt;
			
			var r:int = (angle <= 0) ? -1 : 1;
			var endAngle:Number = startAngle + angle;
			
			var fAngle:Number = startAngle % PI_4;
			if (fAngle * r > 0) {
				fAngle = r * PI_4 - fAngle;
			} else {
				fAngle = -fAngle;
			}
			
			if (Math.abs(fAngle) > Math.abs(angle)) {
				fAngle = angle;
			}
			
			var lAngle:Number;
			
			if (fAngle == angle) {
				lAngle = 0;
			} else {
				lAngle = endAngle % PI_4;
				if (lAngle * r < 0) {
					lAngle += r * PI_4;
				}
			}
			
			var quoterFrom:int = 0;
			var quoterEnd:int = 0;
			var quoterCount:int = Math.round((angle - fAngle - lAngle) / PI_4);
			
			if (quoterCount > 0) {
				quoterFrom = Math.round((startAngle + fAngle) / PI_4);
				if (quoterFrom < 0) {
					quoterFrom = quoterFrom % 8 + 8;
				}
				quoterEnd = quoterFrom + quoterCount;
			} else if (quoterCount < 0) {
				quoterEnd = Math.round((endAngle - lAngle) / PI_4);
				if (quoterEnd < 0) {
					quoterEnd = quoterEnd % 8 + 8;
				}
				quoterFrom = quoterEnd - quoterCount;
			}
			
			drawSmallArc(g, x, y, startAngle, fAngle, radius, yRadius);
			
			while (quoterFrom != quoterEnd) {
				drawQuoterArc(g, x, y, quoterFrom, radius, yRadius, radius / COS_PI_8, yRadius / COS_PI_8, r);
				quoterFrom += r;
			}
			drawSmallArc(g, x, y, endAngle - lAngle, lAngle, radius, yRadius);
			
			return pt;
		}
	}
}